using Abp;
using Abp.Dependency;
using Abp.Threading.Extensions;
using Abp.Threading.Timers;
using Abp.Timing;
using Abp.UI;
using AutoMapper;
using Nito.AsyncEx;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;

namespace AbpProjectTemplate.Utilities.Theading
{
    /// <summary>
    /// 并发控制器
    /// </summary>
    public partial class SemaphoreSlimManager : ISingletonDependency
    {
        public const int DefaultTimeoutSeconds = 300;
        //public const int DefaultInitialCount = 1;//默认初始化数量
        public const int DefaultConcurrentCount = 1;//默认最大数量

        private ConcurrentDictionary<string, Lazy<SemaphoreSlimWrapper>> _dicWrappers = new ConcurrentDictionary<string, Lazy<SemaphoreSlimWrapper>>();

        private SemaphoreSlimWrapper GetWrapper(string key, int concurrentCount = DefaultConcurrentCount)
        {
            var slim = _dicWrappers.GetOrAdd(key, k =>
            {
                return new Lazy<SemaphoreSlimWrapper>(() =>
                    new SemaphoreSlimWrapper(k, concurrentCount));
            }).Value;
            return slim;
        }

        private void OnWaitCompleted(bool enterSuccess)
        {
            if (!enterSuccess)
                throw new UserFriendlyException("请求处理已超时，请稍后重试！");
        }

        private DisposeAction GetDispose(SemaphoreSlimWrapper wrapper, bool removeOnCompleted)
        {
            return new DisposeAction(() =>
            {
                wrapper.Release();
                if (removeOnCompleted)
                {
                    //等待一下再判断，以使等待中的先进入工作中
                    Task.Delay(1).Wait();
                    //删除前提 没有正在完成中的任务
                    if (wrapper.CurrentCount >= wrapper.MaximumCount)
                    {
                        if (_dicWrappers.TryRemove(wrapper.Name, out var slimWrapper))
                        {
                            slimWrapper.Value.Dispose();
                        }
                    }
                }
            });
        }


        /// <summary>
        /// 获取指定key的<see cref="SemaphoreSlim"/>对像，相同名称的对像应用中唯一
        /// </summary>
        /// <param name="key">用于获取或者创建<see cref="SemaphoreSlim"/>对像的key</param>
        /// <param name="concurrentCount">对像的最大并发执行量</param>
        public virtual SemaphoreSlim Get(string key, int concurrentCount = DefaultConcurrentCount)
        {
            var slim = _dicWrappers.GetOrAdd(key, k =>
            {
                return new Lazy<SemaphoreSlimWrapper>(() =>
                    new SemaphoreSlimWrapper(k, concurrentCount));
            }).Value;
            return this.GetWrapper(key, concurrentCount);
        }

        /// <summary>
        /// 排队执行指定key的并发任务
        /// </summary>
        /// <param name="key">排队标识,同一个key控制并发量，不同的key可单独控制</param>
        /// <param name="concurrentCount">最大并发量</param>
        /// <param name="timeoutSeconds">超时时间（秒）</param>
        /// <param name="removeOnCompleted">    
        /// 完成并发后是否删除并发控制对像
        /// 如果不删除,则对像会在程序有效期内一直静态保存
        /// 如果删除,则对像会在并发结束以后dispose掉
        /// </param>
        /// <returns>IDisposable</returns>
        public virtual async Task<IDisposable> LineUpAsync(string key,
                                                         int concurrentCount = DefaultConcurrentCount,
                                                         int timeoutSeconds = DefaultTimeoutSeconds,
                                                         bool removeOnCompleted = false
                                                        )
        {
            var slim = this.GetWrapper(key, concurrentCount);
            this.OnWaitCompleted(await slim.WaitAsync(timeoutSeconds * 1000));
            return this.GetDispose(slim, removeOnCompleted);
        }
        /// <summary>
        /// 排队执行指定key的并发任务
        /// </summary>
        /// <param name="key">排队标识,同一个key控制并发量，不同的key可单独控制</param>
        /// <param name="concurrentCount">最大并发量</param>
        /// <param name="timeoutSeconds">超时时间（秒）</param>
        /// <param name="removeOnCompleted">    
        /// 完成并发后是否删除并发控制对像
        /// 如果不删除,则对像会在程序有效期内一直静态保存
        /// 如果删除,则对像会在并发结束以后dispose掉
        /// </param>
        /// <returns>IDisposable</returns>
        public virtual IDisposable LineUp(string key,
                                        int concurrentCount = DefaultConcurrentCount,
                                        int timeoutSeconds = DefaultTimeoutSeconds,
                                        bool removeOnCompleted = false)
        {
            var slim = this.GetWrapper(key, concurrentCount);
            this.OnWaitCompleted(slim.Wait(timeoutSeconds * 1000));
            return this.GetDispose(slim, removeOnCompleted);
        }
    }
}

